Skip to content

Add 3D cube topology support and wrapping animations#5604

Open
palatter wants to merge 3 commits into
wled:mainfrom
palatter:feature/3d-cube-support
Open

Add 3D cube topology support and wrapping animations#5604
palatter wants to merge 3 commits into
wled:mainfrom
palatter:feature/3d-cube-support

Conversation

@palatter
Copy link
Copy Markdown

@palatter palatter commented May 11, 2026

This PR adds native support for 6-panel 3D cubes to WLED.

Key changes:

  • Topology Wrapping: Implemented applyCubeWrapping in FX_2Dfcn.cpp to allow 2D animations to flow seamlessly across all 6 faces of a cube.
  • Matrix Generator: Added a 'Populate Cube' wizard in the 2D Settings page that automatically configures the correct cross layout.
  • UI/UX: Added a '3D Cube' toggle in 2D settings and a 'Cube' mapping option for segments.
  • Persistence: Full support for saving/loading the cube configuration via JSON.

This enables users with 3D-printed cubes or similar 6-face geometry to use the full library of WLED 2D effects with correct physical behavior.

Summary by CodeRabbit

  • New Features
    • 3D "Cube" mapping mode for LED matrix layouts.
    • Pixel reads/writes respect the cube mapping for correct 3D panel behavior.
    • "3D Cube" checkbox added to 2D settings UI and reflected in export/import.
    • New automatic cube layout generator for quick 6-panel setups.
    • Cube mode preference now saved and restored across restarts.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 11, 2026

Walkthrough

Adds a new M12_Cube 2D mapping with cube-coordinate wrapping, integrates it into pixel read/write paths, persists the isCube flag in config, and exposes UI controls plus a preset generator to create a 6-face cube panel layout.

Changes

Cube Mapping Feature

Layer / File(s) Summary
Data Model & State
wled00/FX.h
Enum mapping1D2D_t adds M12_Cube. Segment declares applyCubeWrapping(int &x, int &y) const. WS2812FX adds isCube bitfield and initializes it in the constructor.
Coordinate Transformation Logic
wled00/FX_2Dfcn.cpp
Adds Segment::applyCubeWrapping() with cube geometry checks and remapping. setPixelColorXY() and getPixelColorXY() invoke the helper before bounds checks when cube mode is active.
Configuration Persistence
wled00/cfg.cpp
Loads hw.led.matrix.isCube into strip.isCube and writes matrix.isCube from strip.isCube when saving matrix settings.
Settings Form Wiring
wled00/set.cpp, wled00/xml.cpp
set.cpp reads ISCB into strip.isCube. xml.cpp emits an ISCB checkbox reflecting strip.isCube in the SUBPAGE_2D form.
Frontend UI & Preset Generator
wled00/data/index.js, wled00/data/settings_2D.htm
Adds "Cube" option to the 2D mapping dropdown, adds a "3D Cube" checkbox, replaces populate action with "Populate Cube", and adds genCube() to auto-create up to six cube-face panels.

Sequence Diagram(s)

(omitted — change is primarily a mapping mode with localized coordinate transformation and UI/config plumbing; a sequence diagram is not required.)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

effect, Awaiting testing

Suggested reviewers

  • Aircoookie
  • DedeHai
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add 3D cube topology support and wrapping animations' directly aligns with the PR's main objective of adding native support for 6-panel 3D cube topologies and implementing topology wrapping for seamless animation flow across cube faces.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (3)
wled00/FX.h (1)

1008-1014: 💤 Low value

Consider packing isCube into the adjacent bitfield struct.

isMatrix is currently a full bool for legacy reasons, but the very next member is a bit-packed struct holding autoSegments, correctWB, and cctFromRgb. Adding isCube as a 1-bit member there (alongside those flags) would save one byte in WS2812FX without changing semantics and keeps cube state colocated with other "strip mode" flags.

♻️ Proposed refactor
     bool isMatrix;
-    bool isCube;
     struct {
       bool autoSegments : 1;
       bool correctWB    : 1;
       bool cctFromRgb   : 1;
+      bool isCube       : 1;
     };

The corresponding initializer in the constructor (line 831) and the cfg.cpp / set.cpp / xml.cpp accessors that read/write strip.isCube will continue to work unchanged.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wled00/FX.h` around lines 1008 - 1014, Replace the standalone bool isCube
with a 1-bit field inside the adjacent anonymous bitfield so it becomes packed
with autoSegments, correctWB and cctFromRgb (e.g., add "bool isCube : 1;" to
that struct and remove the separate "bool isCube;" declaration); leave isMatrix
as-is and keep the same member name isCube so existing constructor
initialization and cfg.cpp / set.cpp / xml.cpp accessors continue to work
unchanged.
wled00/FX_2Dfcn.cpp (2)

188-239: 💤 Low value

If this block is AI-generated, wrap it with the required // AI: markers.

Per the repository coding guidelines, "Embed larger blocks of AI-generated code within // AI: below section was generated by an AI ... // AI: end comments". The cube wrapping logic (with its case-by-case "Top-Left gap", "Bottom-Right gap", etc. comments) looks like a candidate for that convention; if any portion of it was generated by an AI assistant, please wrap the corresponding range accordingly.

As per coding guidelines: "Mark AI-generated code blocks with // AI: below section was generated by an AI / // AI: end comments."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wled00/FX_2Dfcn.cpp` around lines 188 - 239, The Segment::_applyCubeWrapping
function contains a multi-line cube-wrapping implementation that may be
AI-generated; if so, wrap that AI-produced region with the repository's required
markers: insert a comment "// AI: below section was generated by an AI"
immediately before the start of the generated block inside
Segment::_applyCubeWrapping and a matching "// AI: end" immediately after the
end of that block, preserving existing comments and indentation and ensuring the
markers enclose the entire logic from the initial if-check through the final
bounds check so the function remains syntactically unchanged.

188-188: ⚡ Quick win

Consider removing IRAM_ATTR_YN from _applyCubeWrapping to clarify intent, or rely on WLED_SAVE_IRAM flag for IRAM budget protection.

The concern about _applyCubeWrapping's size (~50 lines, multi-branch cube wrapping logic) is valid—it's more complex than the thin wrappers at setPixelColorXY and getPixelColorXY (5–6 lines each). However, WLED_SAVE_IRAM is already enabled in platformio.ini with a comment noting it prevents linker errors, which disables IRAM placement for all three functions by default. This global flag effectively mitigates the IRAM budget concern. For clarity and maintainability, consider either (1) unconditionally removing IRAM_ATTR_YN from _applyCubeWrapping to signal that cube wrapping is not a hot path, or (2) document that the global flag serves as the safety valve. The current state works but relies on the config-level flag rather than explicit code intent.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wled00/FX_2Dfcn.cpp` at line 188, The function Segment::_applyCubeWrapping
currently has the IRAM_ATTR_YN attribute; remove that attribute from the
function signature to clarify intent that this larger, multi-branch helper is
not a hot IRAM-must function (i.e., change "void IRAM_ATTR_YN
Segment::_applyCubeWrapping(...)" to just "void
Segment::_applyCubeWrapping(...)"), or if you prefer to keep it, add a short
comment on top of the function referencing WLED_SAVE_IRAM in platformio.ini as
the intended global IRAM protection so reviewers know the IRAM placement is
controlled centrally; update the declaration and any forward declarations
accordingly and rebuild to ensure no linker/placement regressions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@wled00/data/settings_2D.htm`:
- Around line 122-147: genCube() blindly assumes 6 panels exist and writes
Sf.P0..P5 fields even when addPanel() may stop early due to a lower maxPanels,
causing runtime errors; fix by determining the actual number of panels to create
(e.g., let count = Math.min(6, maxPanels) or by running addPanel until it
returns false and counting successes), set Sf.MPC.value = count, call addPanel()
exactly count times, and only write Sf.P{i}X/P{i}Y and
Sf["P"+i+"W"]/Sf["P"+i+"H"] for i in [0..count-1] (leave or clear remaining
fields as appropriate) so genCube(), addPanel(), Sf.MPC and the P0..P5
assignments stay consistent.

In `@wled00/FX_2Dfcn.cpp`:
- Around line 188-190: The hot-path cost comes from calling
Segment::_applyCubeWrapping out-of-line on every pixel; change callers
Segment::setPixelColorXY and Segment::getPixelColorXY to inline the cheap
is-cube check (e.g. if (strip.isCube) _applyCubeWrapping(x,y);) and move the
heavier dimension validation into _applyCubeWrapping itself; inside
_applyCubeWrapping cache n = vHeight() / 3 (and derived 2*n, 4*n as needed)
instead of recomputing vHeight()/3 multiple times, and optionally add a
precomputed boolean (updated when geometry or strip.isCube changes) that encodes
"cube layout valid" so per-pixel code only does a single flag check.
- Line 190: The dimension check in the conditional using strip.isCube and
vWidth/vHeight allows non-multiples-of-3 heights due to integer truncation;
update the guard in FX_2Dfcn.cpp so it returns early unless strip.isCube is true
AND vHeight() is divisible by 3 and vWidth() equals (vHeight()/3)*4 (i.e. add
vHeight() % 3 == 0 into the condition), ensuring the remapping logic that
assumes vHeight() == 3*n only runs for valid cube-net dimensions.
- Around line 236-239: The final unconditional clamps for x and y in the
wrapping logic (the lines that force x into [0,4*n) and y into [0,3*n)) should
be removed so out-of-net coordinates remain out of range and are dropped by the
caller’s unsigned-range check; locate the bounds-clamping code in FX_2Dfcn.cpp
(the block that does "if (x < 0) x = 0; else if (x >= 4 * n) x = 4 * n - 1;" and
the analogous y line) and delete those two clamp statements (or replace them
with a no-op/early return that leaves x/y unchanged) so stray writes are not
snapped to the nearest face edge and are instead discarded by the caller.
- Around line 188-239: The cube-wrapping function _applyCubeWrapping currently
only checks strip.isCube and global aspect ratio, so per-segment mapping1D2D_t
(M12_Cube) is ignored; gate the remap by checking the segment's mapping
selection before applying the transform. Update Segment::_applyCubeWrapping to
early-return unless both strip.isCube and this->mapping1D2D == M12_Cube (or the
segment-level flag/name you use for per-segment mapping), or alternatively
remove M12_Cube from mapping1D2D_t and the UI if cube behavior is truly
strip-wide; refer to symbols _applyCubeWrapping, strip.isCube, mapping1D2D_t,
M12_Cube and any segment field like mapping1D2D or map1D2D to locate the change.
Ensure tests/usage paths that expect flat 2D semantics for segments not set to
M12_Cube remain unchanged.

In `@wled00/FX.h`:
- Line 550: The current cube-wrapping logic in _applyCubeWrapping (declared in
FX.h and implemented in FX_2Dfcn.cpp) ignores the per-segment map1D2D setting
and instead uses strip.isCube and aspect checks (vWidth(), vHeight()), which
makes the per-segment enum value M12_Cube ineffective; fix this by gating the
cube remapping so it only runs when the segment's map1D2D == M12_Cube (or, if
cube wrapping is meant to be a strip-global feature, remove M12_Cube from the
per-segment enum and related UI option), updating the implementation in
FX_2Dfcn.cpp to consult the Segment::map1D2D before applying cube wrapping and
leaving strip.isCube/aspect checks as supplementary conditions.

---

Nitpick comments:
In `@wled00/FX_2Dfcn.cpp`:
- Around line 188-239: The Segment::_applyCubeWrapping function contains a
multi-line cube-wrapping implementation that may be AI-generated; if so, wrap
that AI-produced region with the repository's required markers: insert a comment
"// AI: below section was generated by an AI" immediately before the start of
the generated block inside Segment::_applyCubeWrapping and a matching "// AI:
end" immediately after the end of that block, preserving existing comments and
indentation and ensuring the markers enclose the entire logic from the initial
if-check through the final bounds check so the function remains syntactically
unchanged.
- Line 188: The function Segment::_applyCubeWrapping currently has the
IRAM_ATTR_YN attribute; remove that attribute from the function signature to
clarify intent that this larger, multi-branch helper is not a hot IRAM-must
function (i.e., change "void IRAM_ATTR_YN Segment::_applyCubeWrapping(...)" to
just "void Segment::_applyCubeWrapping(...)"), or if you prefer to keep it, add
a short comment on top of the function referencing WLED_SAVE_IRAM in
platformio.ini as the intended global IRAM protection so reviewers know the IRAM
placement is controlled centrally; update the declaration and any forward
declarations accordingly and rebuild to ensure no linker/placement regressions.

In `@wled00/FX.h`:
- Around line 1008-1014: Replace the standalone bool isCube with a 1-bit field
inside the adjacent anonymous bitfield so it becomes packed with autoSegments,
correctWB and cctFromRgb (e.g., add "bool isCube : 1;" to that struct and remove
the separate "bool isCube;" declaration); leave isMatrix as-is and keep the same
member name isCube so existing constructor initialization and cfg.cpp / set.cpp
/ xml.cpp accessors continue to work unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 78a5cdf2-3fe7-4e00-9062-7cad3f9f2d15

📥 Commits

Reviewing files that changed from the base of the PR and between f58187e and 65c8524.

📒 Files selected for processing (7)
  • wled00/FX.h
  • wled00/FX_2Dfcn.cpp
  • wled00/cfg.cpp
  • wled00/data/index.js
  • wled00/data/settings_2D.htm
  • wled00/set.cpp
  • wled00/xml.cpp

Comment thread wled00/data/settings_2D.htm
Comment thread wled00/FX_2Dfcn.cpp Outdated
Comment thread wled00/FX_2Dfcn.cpp Outdated
Comment thread wled00/FX_2Dfcn.cpp Outdated
Comment thread wled00/FX_2Dfcn.cpp Outdated
Comment thread wled00/FX.h Outdated
… gating/markers, and improve genCube robustness
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@wled00/FX_2Dfcn.cpp`:
- Around line 211-239: The fallback mapping for the back-column gap (when y < n
and fx is not 0/1/2) leaves x in [3*n,4*n) (a gap) instead of remapping into the
Top face span [n,2*n), and there's no symmetric fx==3 branch for the Bottom
side; update the fallback in the "Moving UP" block so the back-gap maps into the
Top face coordinates (use x = 2*n - 1 - u and y = n - 1 - v or equivalent to
place x into [n,2*n) and y into [0,n)), and add a matching branch in the "Moving
DOWN" block (for fx == 3) that maps the back-column gap into the Bottom face
span [n,2*n) (mirror the coordinate transforms using u/v and n so x falls into
[n,2*n) and y into [2*n,3*n) as appropriate); refer to variables fx, x, y, u, v,
and n to locate and adjust the transformations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 61b6b891-3532-4380-bd54-6ce65af733a3

📥 Commits

Reviewing files that changed from the base of the PR and between 65c8524 and 0a72928.

📒 Files selected for processing (3)
  • wled00/FX.h
  • wled00/FX_2Dfcn.cpp
  • wled00/data/settings_2D.htm
🚧 Files skipped from review as they are similar to previous changes (2)
  • wled00/data/settings_2D.htm
  • wled00/FX.h

Comment thread wled00/FX_2Dfcn.cpp Outdated
Comment thread wled00/FX.h Outdated
@softhack007
Copy link
Copy Markdown
Member

This PR adds native support for 6-panel 3D cubes to WLED.

@palatter just an idea - can you also support 5-panel cubes (bottom panel missing because invisible) and 3- or 4-panel cubes (backfaces missing)? "Different problem" would be an acceptable answer - I just remember from the past that some users succeeded with these "lightweight cube" variants by directly applying a custom ledmap.json file.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@wled00/FX_2Dfcn.cpp`:
- Around line 188-223: The fx != 1 checks in Segment::applyCubeWrapping silently
remap out-of-range x values (fx == -1 or fx >= 4) instead of letting the caller
drop them; change both occurrences inside the fy == 0 and fy == 2 branches to
test fx == 3 (the intended "Back" gap) so only the legitimate back-gap case is
remapped and true out-of-range coordinates fall through to the caller's bounds
check; update the two branches that currently say "fx != 1" to "fx == 3"
(keeping the same remap assignments to x and y).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8fee6f00-0e9d-4ffa-8ff0-7a264721cbc3

📥 Commits

Reviewing files that changed from the base of the PR and between 0a72928 and 55c4f92.

📒 Files selected for processing (2)
  • wled00/FX.h
  • wled00/FX_2Dfcn.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
  • wled00/FX.h

Comment thread wled00/FX_2Dfcn.cpp
Comment on lines +188 to +223
void Segment::applyCubeWrapping(int &x, int &y) const
{
// AI: below section was generated by an AI
if (!strip.isCube || map1D2D != M12_Cube) return;
const uint16_t vH = vHeight();
const uint16_t vW = vWidth();
if (vH % 3 != 0 || vW != (vH / 3) * 4) return;

int n = vH / 3;
int fx = (x >= 0) ? (x / n) : -1;
int fy = (y >= 0) ? (y / n) : -1;
int u = (x % n + n) % n;
int v = (y % n + n) % n;

if (fy < 0 || fy > 2) { // Above Top or Below Bottom
x = 4 * n - 1 - u;
y = 2 * n - 1 - v;
} else if (fy == 0) {
if (fx == 0) { // Top-Left gap -> maps to Top face left edge
x = n + (n - 1 - v); y = u;
} else if (fx == 2) { // Top-Right gap -> maps to Top face right edge
x = n + v; y = n - 1 - u;
} else if (fx != 1) { // fx == 3 (Top-Back gap) -> maps to Top face top edge
x = 2 * n - 1 - u; y = n - 1 - v;
}
} else if (fy == 2) {
if (fx == 0) { // Bottom-Left gap -> maps to Bottom face left edge
x = n + v; y = 3 * n - 1 - u;
} else if (fx == 2) { // Bottom-Right gap -> maps to Bottom face right edge
x = 2 * n - 1 - v; y = 2 * n + u;
} else if (fx != 1) { // fx == 3 (Bottom-Back gap) -> maps to Bottom face bottom edge
x = 2 * n - 1 - u; y = 3 * n - 1 - v;
}
}
// AI: end
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

fx != 1 branches silently remap out-of-range x instead of letting the caller drop it.

Inside the fy == 0 and fy == 2 blocks, the third branch uses fx != 1 with a comment indicating the intent is fx == 3 (Top/Bottom-Back gap). However, fx can also be -1 (when x < 0) or >= 4 (when x >= 4*n), because nothing earlier in the function bounds x. For those cases, this branch still fires and remaps the coordinate onto a valid Top/Bottom face cell:

  • x = -1, y in [0, n)fx = -1, hits fx != 1, mapped onto top face's left column.
  • x >= 4n, y in [0, n)fx >= 4, hits fx != 1, mapped onto top face.
  • Symmetric cases for fy == 2.

This contradicts the design that out-of-cube-net coordinates should remain out of range so the caller's (unsigned)x >= vWidth() check discards them (the same rationale used to remove the earlier [0,4n) x [0,3n) clamp). Effects that occasionally probe just off the segment edges with cube mode active may paint stray pixels onto Top/Bottom face borders.

Tightening to an exact fx == 3 keeps the back-gap fix but lets truly out-of-range writes/reads fall through to the caller's bounds check:

🛠️ Proposed fix
   } else if (fy == 0) {
     if (fx == 0) { // Top-Left gap -> maps to Top face left edge
       x = n + (n - 1 - v); y = u;
     } else if (fx == 2) { // Top-Right gap -> maps to Top face right edge
       x = n + v; y = n - 1 - u;
-    } else if (fx != 1) { // fx == 3 (Top-Back gap) -> maps to Top face top edge
+    } else if (fx == 3) { // Top-Back gap -> maps to Top face top edge
       x = 2 * n - 1 - u; y = n - 1 - v;
     }
   } else if (fy == 2) {
     if (fx == 0) { // Bottom-Left gap -> maps to Bottom face left edge
       x = n + v; y = 3 * n - 1 - u;
     } else if (fx == 2) { // Bottom-Right gap -> maps to Bottom face right edge
       x = 2 * n - 1 - v; y = 2 * n + u;
-    } else if (fx != 1) { // fx == 3 (Bottom-Back gap) -> maps to Bottom face bottom edge
+    } else if (fx == 3) { // Bottom-Back gap -> maps to Bottom face bottom edge
       x = 2 * n - 1 - u; y = 3 * n - 1 - v;
     }
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wled00/FX_2Dfcn.cpp` around lines 188 - 223, The fx != 1 checks in
Segment::applyCubeWrapping silently remap out-of-range x values (fx == -1 or fx
>= 4) instead of letting the caller drop them; change both occurrences inside
the fy == 0 and fy == 2 branches to test fx == 3 (the intended "Back" gap) so
only the legitimate back-gap case is remapped and true out-of-range coordinates
fall through to the caller's bounds check; update the two branches that
currently say "fx != 1" to "fx == 3" (keeping the same remap assignments to x
and y).

@softhack007 softhack007 added this to the 17.0 milestone May 11, 2026
@softhack007
Copy link
Copy Markdown
Member

@palatter general question: would it be more efficient maybe to pre-calculate the complete mapping into customMappingTable[] (a.k.a. "ledmap"), instead of re-calculating for each pixel being painted?

@palatter
Copy link
Copy Markdown
Author

palatter commented May 11, 2026

@palatter general question: would it be more efficient maybe to pre-calculate the complete mapping into customMappingTable[] (a.k.a. "ledmap"), instead of re-calculating for each pixel being painted?

That's a good idea! I'll investigate that. I think it would be trade off of cpu vs memory and likely add more complexity.

@willmmiles
Copy link
Copy Markdown
Member

I concur with @softhack007 -- an ledmap calculator that doesn't require any logic changes is the best approach. Ideally, it could be implemented entirely in JS in your own repo, and integrated for optional download via the wled-web-tools repository. Basically you make a standalone .htm page, and the web tools repo lets users download it to their WLED install via the Pixelforge system UI (the hammer icon).

@softhack007
Copy link
Copy Markdown
Member

Possibly related: an xls sheet that calculates a ledmap.json for some cubes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants